﻿namespace NJQuery {
	using System;
	using System.Collections.ObjectModel;
	using System.Web.UI;

	/// <summary>
	/// Manages a grouped set of controls for a container control
	/// </summary>
	/// <typeparam name="T">The control type conained in the collection.</typeparam>
	public abstract class GroupControlCollection<T> : Collection<T> where T : Control {


		#region Fields

		private GroupControlContainer container;
		private bool insertLiteralBefore;
		private bool insertLiteralAfter;
		private int containerBlockCount;

		#endregion


		#region Constructors

		/// <summary>
		/// Initializes a new instance of the <see cref="GroupControlCollection{T}"/> class.
		/// </summary>
		/// <param name="container">The group control that the set of controls belongs to.</param>
		/// <exception cref="ArgumentNullException">Container cannot be null.</exception>
		protected GroupControlCollection(GroupControlContainer container) : this(container, false, false) { }

		/// <summary>
		/// Initializes a new instance of the <see cref="GroupControlCollection{T}"/> class with options to surround the controls with <see cref="LiteralControl"/> objects for further HTML markup outside the server controls.
		/// </summary>
		/// <param name="container">The group control that the set of controls belongs to.</param>
		/// <param name="insertLiteralBefore">A True value will place a new <see cref="LiteralControl"/> before each server control for further HTML markup.</param>
		/// <param name="insertLiteralAfter">A True value will place a new <see cref="LiteralControl"/> after each server control for further HTML markup.</param>
		/// <exception cref="ArgumentNullException">Container cannot be null.</exception>
		protected GroupControlCollection(GroupControlContainer container, bool insertLiteralBefore, bool insertLiteralAfter) {
			if(container == null) throw new ArgumentNullException("container");

			this.container = container;
			this.insertLiteralBefore = insertLiteralBefore;
			this.insertLiteralAfter = insertLiteralAfter;

			containerBlockCount = 1;
			if(insertLiteralBefore) containerBlockCount++;
			if(insertLiteralAfter) containerBlockCount++;
		}

		#endregion


		#region Properties

		/// <summary>
		/// Gets the control containing the grouped set of controls.
		/// </summary>
		public virtual GroupControlContainer Container {
			get { return container; }
		}

		#endregion


		#region Methods: Collection modification

		/// <summary>
		/// Inserts an element into the <see cref="AccordionPanelCollection"/> at the specified index.
		/// </summary>
		/// <param name="index">The zero-based index at which item should be inserted.</param>
		/// <param name="item">The object to insert.</param>
		/// <exception cref="ArgumentOutOfRangeException">Index must be non-negative and no bigger than Count.</exception>
		/// <exception cref="ArgumentNullException">Item is required.</exception>
		protected override void InsertItem(int index, T item) {
			if(index < 0 || index > Count) throw new ArgumentOutOfRangeException("index");
			if(item == null) throw new ArgumentNullException("item");

			int containerIndex = index * containerBlockCount;
			ControlCollection containerControls = container.Controls;

			IGroupContainedControl groupContained = item as IGroupContainedControl;
			if(groupContained != null) groupContained.Container = container;

			if(insertLiteralBefore) containerControls.AddAt(containerIndex++, new LiteralControl());
			containerControls.AddAt(containerIndex++, item);
			if(insertLiteralAfter) containerControls.AddAt(containerIndex, new LiteralControl());

			base.InsertItem(index, item);
		}

		/// <summary>
		/// Removes all elements from the <see cref="AccordionPanelCollection"/>.
		/// </summary>
		protected override void ClearItems() {
			base.ClearItems();
			container.Controls.Clear();
		}

		/// <summary>
		/// Removes the item at the specified index of the <see cref="AccordionPanelCollection"/>.
		/// </summary>
		/// <param name="index">The zero-based index of the element to remove.</param>
		/// <exception cref="ArgumentOutOfRangeException">Index must be non-negative and less than Count.</exception>
		protected override void RemoveItem(int index) {
			if(index < 0 || index >= Count) throw new ArgumentOutOfRangeException("index");

			base.RemoveItem(index);

			int containerIndex = index * containerBlockCount;
			ControlCollection containerControls = container.Controls;

			if(insertLiteralBefore) containerControls.RemoveAt(containerIndex++);
			container.Controls.RemoveAt(containerIndex++);
			if(insertLiteralAfter) containerControls.RemoveAt(containerIndex);
		}

		/// <summary>
		/// Replaces the element at the specified index.
		/// </summary>
		/// <param name="index">The zero-based index of the element to replace.</param>
		/// <param name="item">The new value for the element at the specified index.</param>
		/// <exception cref="ArgumentOutOfRangeException">Index must be non-negative and less than Count.</exception>
		/// <exception cref="ArgumentNullException">Item is required.</exception>
		protected override void SetItem(int index, T item) {
			if(index < 0 || index >= Count) throw new ArgumentOutOfRangeException("index");
			if(item == null) throw new ArgumentNullException("item");

			int containerIndex = index * containerBlockCount;
			ControlCollection containerControls = container.Controls;

			IGroupContainedControl groupContained = item as IGroupContainedControl;
			if(groupContained != null) groupContained.Container = container;

			if(insertLiteralBefore) containerControls.AddAt(containerIndex++, new LiteralControl());
			containerControls.AddAt(containerIndex++, item);
			if(insertLiteralAfter) containerControls.AddAt(containerIndex++, new LiteralControl());

			base.SetItem(index, item);

			if(insertLiteralBefore) containerControls.RemoveAt(containerIndex++);
			containerControls.RemoveAt(containerIndex++);
			if(insertLiteralAfter) containerControls.RemoveAt(containerIndex);
		}

		#endregion


		#region Methods: Public

		/// <summary>
		/// Searches for the first control in the collection with the specified Client ID.
		/// </summary>
		/// <param name="clientId">The Client ID to search for.</param>
		/// <returns>The first matching control or null for no matches.</returns>
		public virtual T FindByClientId(string clientId) {
			foreach(T item in this)
				if(item.ClientID == clientId) return item;
			return null;
		}

		#endregion


	}

}